iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
自我挑戰組

模仿知名網站的外觀系列 第 5

【Day5】模仿知名網站的外觀 Instagram(5) 完成個人檔案頁面

  • 分享至 

  • xImage
  •  

接下來,我們會先寫出Profile的上半部分。

我們使用Pexels圖庫,這是一個CC0圖庫,不需要擔心侵權的問題。

來到ProfileUserDetails.jsx,開始編寫:

import React from "react";
import { TbCircleDashed } from "react-icons/tb";

export const ProfileUserDetails = () => {
	return (
		<div className="py-10 w-full">
			<div className="flex items-center">
				<div className="w-[15%]">
					<img
						className="w-32 h-32 rounded-full"
						src="https://images.pexels.com/photos/1128121/pexels-photo-1128121.jpeg?auto=compress"
						alt=""
					/>
				</div>
				<div>
					<div className="flex space-x-10 items-center">
						<p>username</p>
						<button>Edit Profile</button>
						<TbCircleDashed></TbCircleDashed>
					</div>
				</div>
			</div>
		</div>
	);
};

export default ProfileUserDetails;

py-10:是設定上下padding的距離。

rounded-full:讓邊邊是圓的。

space-x-10:設定左右的margin。

其餘的在之前的章節有遇到過或是遇到類似的,就不再介紹了。

修改Profile.jsx,顯示剛寫好的ProfileUserDetails。

import React from "react";
import ProfileUserDetails from "../../Components/ProfileComponents/ProfileUserDetails";

const Profile = () => {
	return (
		<div className="px-20">
			<div>
				<ProfileUserDetails />
			</div>
		</div>
	);
};

export default Profile;

啟動專案,看到以下的畫面是正常的,因為父元素沒有足夠的寬度來顯示完整的圓。

來到Router.jsx,修改在前的div寬度。

<div className="w-full">
          <Routes>

我們就能見到完美的圓了。

繼續完善ProfileUserDetails.jsx,添加一些內容。

<div className="space-y-5">
	<div className="flex space-x-10 items-center">
		<p>username</p>
		<button>Edit Profile</button>
		<TbCircleDashed></TbCircleDashed>
	</div>
	
	<div className="flex space-x-10">
		<div>
			<span className="font-semibold mr-2">19</span>
			<span>posts</span>
		</div>

		<div>
			<span className="font-semibold mr-2">19</span>
			<span>followers</span>
		</div>

		<div>
			<span className="font-semibold mr-2">810</span>
			<span>following</span>
		</div>
	</div>
</div>

space-y-5:設定上下的margin。

啟動專案後,看到以下的畫面,代表成功添加內容。

接下來完成上半區塊,完整的程式碼如下:

import React from "react";
import { TbCircleDashed } from "react-icons/tb";

export const ProfileUserDetails = () => {
	return (
		<div className="py-10 w-full">
			<div className="flex items-center">
				<div className="w-[15%]">
					<img
						className="w-32 h-32 rounded-full"
						src="https://images.pexels.com/photos/1128121/pexels-photo-1128121.jpeg?auto=compress"
						alt=""
					/>
				</div>
				<div className="space-y-5">
					<div className="flex space-x-10 items-center">
						<p>username</p>
						<button>Edit Profile</button>
						<TbCircleDashed></TbCircleDashed>
					</div>

					<div className="flex space-x-10">
						<div>
							<span className="font-semibold mr-2">19</span>
							<span>posts</span>
						</div>

						<div>
							<span className="font-semibold mr-2">19</span>
							<span>followers</span>
						</div>

						<div>
							<span className="font-semibold mr-2">810</span>
							<span>following</span>
						</div>
					</div>

					<div>
						<p className="font-semibold">Full Name</p>
						<p className="font-thin text-sm">
							🔥 Living my best life with no regrets 🔥
						</p>
					</div>
				</div>
			</div>
		</div>
	);
};

export default ProfileUserDetails;

接下來,我們要做出選擇Posts、Reels、Saved、Tagged的地方。

在ProfileComponents下,新增ProfileUserPostBar.jsx。

import React, { useState } from "react";
import { AiOutlineTable, AiOutlineUser } from "react-icons/ai";
import { RiVideoAddLine } from "react-icons/ri";
import { BiBookmark } from "react-icons/bi";

const ProfileUserPostBar = () => {
	const [activeTab, setActiveTab] = useState([]);

	const tabs = [
		{
			tab: "Post",
			icon: <AiOutlineTable></AiOutlineTable>,
			activeTab: "",
		},

		{
			tab: "Reels",
			icon: <RiVideoAddLine></RiVideoAddLine>,
		},

		{
			tab: "Saved",
			icon: <BiBookmark></BiBookmark>,
		},

		{
			tab: "Tagged",
			icon: <AiOutlineUser></AiOutlineUser>,
		},
	];
	return (
		<div className="flex space-x-14 border-t relative">
			{tabs.map((item) => (
				<div
					onClick={() => setActiveTab(item.tab)}
					className={`${
						activeTab === item.tab ? "border-t border-black" : "opacity-60"
					} flex items-center cursor-pointer py-2 text-sm`}
				>
					<p>{item.icon}</p>
					<p className="ml-1">{item.tab}</p>
				</div>
			))}
		</div>
	);
};

export default ProfileUserPostBar;

border-t:代表只有上方才有邊界線。

relative:會根據flex的位置而有所變化。

border-black:邊界線設定成黑色。

opacity-60:設定透明度為60。另外,完全透明為0,不透明是100。

來到Profile.jsx,顯示ProfileUserPostBar的內容。

import React from "react";
import ProfileUserDetails from "../../Components/ProfileComponents/ProfileUserDetails";
import ProfileUserPostBar from "../../Components/ProfileComponents/ProfileUserPostBar";

const Profile = () => {
	return (
		<div className="px-20">
			<div>
				<ProfileUserDetails />
			</div>
			<div>
				<ProfileUserPostBar />
			</div>
		</div>
	);
};

export default Profile;

嘗試按下Posts、Reels、Saved、Tagged並觀察它們的變化。

接下來寫Post的部分。

在ProfileComponents下,新增ProfileUserPostCard.jsx,用來呈現Posts底下的一個Post的內容縮圖以及Like和留言數。

import React from "react";
import { AiFillHeart } from "react-icons/ai";
import { FaComment} from "react-icons/fa";

const ProfileUserPostCard = () => {
	return (
		<div>
			<div className="w-60 h-60">
				<img className="cursor-pointer" src="https://images.pexels.com/photos/18133619/pexels-photo-18133619.png" alt="" />
				<div>
					<div>
						<div>
							<AiFillHeart></AiFillHeart>
							<span>10</span>
						</div>
						<div>
							<FaComment />
							<span>30</span>
						</div>
					</div>
				</div>
			</div>
		</div>
	);
};

export default ProfileUserPostCard;

回到ProfileUserPostBar.jsx,顯示貼文的效果。

先引入ProfileUserPostCard

import ProfileUserPostCard from "./ProfileUserPostCard";

再來去修改return區塊的內容:

<div>
	<div className="flex space-x-14 border-t relative">
		{tabs.map((item) => (
			<div
				onClick={() => setActiveTab(item.tab)}
				className={`${
					activeTab === item.tab ? "border-t border-black" : "opacity-60"
				} flex items-center cursor-pointer py-2 text-sm`}
			>
				<p>{item.icon}</p>
				<p className="ml-1">{item.tab}</p>
			</div>
		))}
	</div>
	<div>
        <div className="flex flex-wrap">
            {[1, 1, 1, 1, 1].map((item) => <ProfileUserPostCard />)}
        </div>
    </div>
</div>

flex-wrap:允許多行顯示。

在一般情況下看不出變化的人,可以嘗試放大及縮小網頁,就能看到部分內容會到第二第三行。

沒有縮放的情況下,網頁是這樣的。

接著,我們會將Like數、留言數在正常情況下隱藏起來,當滑鼠放在圖片上面時才會顯示。

在ProfileComponents下,新增ProfileUserPostCard.css,這只用在ProfileUserPostCard.jsx上。

.post{
    position: relative;
    overflow: hidden;
}
.post img{
    display: block;
    width: 100%;
    height: 100%;
}
.post:hover .overlay{
    opacity: 1;
}

.overlay{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    opacity: 0;
    transition: opacity .2s ease-in-out;
}

.overlay-text{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: white;
    font-size: medium;
    text-align: center;
    width: 30%;
}

overflow: hidden代表元素超出範圍時會直接消失。

display: block代表元素會以矩形呈現。

background-color: rgba(0, 0, 0, 0.5)代表背景是半透明的黑色。

transition: opacity .2s ease-in-out:產生轉場動畫,當opacity發生變化時才會有動作,持續0.2秒,採用先快後慢的變化。

transform: translate(-50%, -50%):把元素沿著 x 軸向左移動 50% 的自身寬度,沿著 y 軸向上移動 50% 的自身高度,就能將元素置中。

回到ProfileUserPostCard.jsx,引用css,修改部分代碼:

import React from "react";
import { AiFillHeart } from "react-icons/ai";
import { FaComment} from "react-icons/fa";
import './ProfileUserPostCard.css';

const ProfileUserPostCard = () => {
	return (
		<div className="p-2">
			<div className="post w-60 h-60">
				<img className="cursor-pointer" src="https://images.pexels.com/photos/18133619/pexels-photo-18133619.png" alt="" />
				<div className="overlay">
					<div className="overlay-text flex justify-between">
						<div>
							<AiFillHeart></AiFillHeart>
							<span>10</span>
						</div>
						<div>
							<FaComment />
							<span>30</span>
						</div>
					</div>
				</div>
			</div>
		</div>
	);
};

export default ProfileUserPostCard;

把滑鼠移動到元素上就能看到我們改動的作用了。


上一篇
【Day4】模仿知名網站的外觀 Instagram(4) 設定側邊欄的按鈕事件與個人檔案頁面
下一篇
【Day6】模仿知名網站的外觀 Instagram(6) 編寫Instagram首頁
系列文
模仿知名網站的外觀30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言